본문으로 건너뛰기
버전: 3.3.1

Firebase Storage CORS 에러

프록시 구조를 검토하는 과정에서, 서버가 Signed URL만 발급하고 실제 다운로드 요청은 Firebase Storage로 직접 전달되면서 발생한 문제입니다.

영상 재생은 가능했지만 fetch 기반 다운로드에서는 CORS 정책이 적용되었고, 이 차이를 이해하는 과정에서 브라우저의 리소스 로딩 방식과 직접 데이터 요청 방식의 차이를 확인할 수 있었습니다.



개요

프록시 기반 다운로드 구조를 검토하는 과정에서 Firebase Storage 다운로드 요청에 대해서만 CORS 에러가 발생하는 문제를 경험하였습니다.

특히 동일한 Signed URL을 사용하고 있었음에도

  • 영상 재생은 정상 동작
  • 다운로드는 실패

하는 현상이 발생하였으며, 초기에는 그 원인을 파악하기 어려웠습니다.

해당 문제를 분석하는 과정에서 브라우저의 리소스 로딩 방식과 데이터 요청 방식의 차이를 이해할 수 있었으며, 결과적으로 구조적인 문제 역시 함께 발견할 수 있었습니다.


(구) 프록시 기반 다운로드 구조

초기에는 Firebase Storage의 실제 경로와 파일 식별자를 외부에 직접 노출하지 않기 위해 프록시 기반 구조를 검토하였습니다.

당시 구상한 구조는 다음과 같습니다.

Client

Proxy Server

Firebase Storage

사용자가 영상을 요청하면, 우선 서버에서 해당 요청이 유효한지 검증을 수행한 뒤 스토리지의 파일에 접근하는 방식입니다.

이 구조의 가장 큰 장점은 모든 요청이 서버를 거치기 때문에

  • 접근 제어
  • 다운로드 횟수 제한
  • 비정상 요청 차단

과 같은 정책을 중앙에서 관리할 수 있다는 점이었습니다.

그러나 실제 구현 과정에서 프록시 구조에 대한 이해가 부족하였고, Firebase Storage의 Signed URL을 함께 활용하면서 의도했던 구조와는 다른 형태로 발전하게 되었습니다.

결과적으로 서버는 파일을 직접 전달하는 대신 Signed URL을 발급하는 역할만 수행하게 되었고, 클라이언트는 해당 URL을 이용하여 Firebase Storage에 직접 접근하게 되었습니다.

Client

Proxy Server

Signed URL 발급

Client

Firebase Storage

당시에는 해당 차이를 명확하게 인지하지 못하였으나, 이 구조가 이후 CORS 문제의 원인이 되었습니다.


문제 발생

초반 프록시 구조를 처음 사용해보는 과정에서 아키텍처를 잘못 설계하여 Signed URL과 프록시를 병행 사용하는 구조로 구현하였습니다.

원래 의도는 프록시 서버가 Firebase Storage의 파일을 직접 조회한 뒤 클라이언트에게 전달하는 구조였습니다.

그러나 실제 구현에서는 서버가 Signed URL만 발급하고, 클라이언트가 해당 URL을 이용하여 Firebase Storage에 직접 접근하는 형태가 되었습니다.

그 결과 다운로드 기능을 구현하는 과정에서 브라우저는 Firebase Storage로 직접 fetch 요청을 전송하게 되었고, 이 과정에서 CORS 에러가 발생하였습니다.

당시에는 프록시 서버가 존재하기 때문에 해당 요청 역시 동일한 도메인으로 처리될 것이라 생각하였으나, 실제로는 브라우저가 Firebase Storage를 별개의 Origin으로 판단하고 있었습니다.


왜 영상은 재생되고 다운로드만 실패하였는가

초기에 가장 이해하기 어려웠던 부분은 영상은 정상적으로 재생되는데 다운로드만 실패한다는 점이었습니다.

당시에는 동일한 Signed URL을 사용하고 있었기 때문에, 두 기능이 동일하게 동작할 것이라 생각하였습니다.

그러나 실제로는 브라우저가 두 요청을 다르게 처리하고 있었습니다.

영상 재생의 경우 HTML의 video 태그를 통해 단순 리소스 조회가 이루어지며, 브라우저는 이를 일반적인 미디어 스트리밍 요청으로 처리합니다.

반면 다운로드 기능은 fetch를 통해 데이터를 직접 읽어온 뒤 Blob으로 변환하는 방식으로 구현되어 있었습니다.

이 경우 브라우저는 단순 리소스 조회가 아닌 JavaScript에 의한 데이터 접근으로 판단하며, CORS 정책을 엄격하게 적용하게 됩니다.

결과적으로 동일한 URL을 사용하더라도

  • video 태그 기반 재생은 가능
  • fetch 기반 다운로드는 차단

이라는 현상이 발생하게 되었습니다.

이를 통해 브라우저의 리소스 로딩과 데이터 요청이 서로 다른 보안 정책으로 처리된다는 사실을 확인할 수 있었습니다.


정리

당시에는 Firebase Storage의 CORS 설정을 수정하여 문제를 해결할 수 있었습니다.

그러나 이후 서비스 구조를 재검토하는 과정에서 프록시와 Signed URL을 동시에 사용하는 방식 자체가 불필요하게 복잡한 구조라는 점을 확인하였습니다.

결과적으로 해당 프록시 구조는 폐기되었으며, 현재는 Signed URL 기반 접근 제어 구조를 사용하고 있습니다.

다만 본 트러블슈팅은 단순히 CORS 설정 방법을 기록하기 위한 문서라기보다는, 브라우저의 리소스 요청 방식과 데이터 요청 방식의 차이를 이해하게 된 사례로 남아 있습니다.